.env#
# .env
NEXT_PUBLIC_NOTION_OAUTH_CLIENT_ID=<client-id>
NOTION_OAUTH_CLIENT_SECRET=<client-secret>
REDIRECT_URL=<client-redirect-url>
Auth Flows#
1. install Integration#
integration access page
- display the permissions of the integration
- select access pages
Result#
- Cancel the access
- Allow the access: with selected pages
- all pages
- some of the pages
- no pages
Situations should be handled#
1. user not access all pages#
2. back to redirect_uri#
the page handle the integration install result
$REDIRECT_URL?code=03ae371f-3e6d-4b11-9c93-e9013ae47795&status=
Notion will redirect to the redirect_uri with different url parameters for the different install results:
code
&state
: Allow the accesserror
: Cancel the access
Situations should be handled#
1. The display for Access Denied#
2. The display for Access Granted#
3. Get Auth Token#
use Notion API to get access token with the
code
parameter
const clientId = NEXT_PUBLIC_NOTION_OAUTH_CLIENT_ID;
const clientSecret = NOTION_OAUTH_CLIENT_SECRET;
const redirectUri = REDIRECT_URL;
const codeToToken = async (code: string) => {
const encoded = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
const response = await fetch("https://api.notion.com/v1/oauth/token", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Basic ${encoded}`,
},
body: JSON.stringify({
grant_type: "authorization_code",
code: code,
redirect_uri: redirectUri,
}),
}).then((res) => res.json());
const token = response?.access_token;
return token;
};
Other Flows#
- List Pages#
- use Notion API and token to list pages
type Parent =
| {
type: "workspace";
workspace: true;
}
| {
type: "database_id";
database_id: string;
}
| {
type: "page_id";
page_id: string;
}
| {
type: "block_id";
block_id: string;
};
type Page = {
id: string;
parent: Parent;
title: string;
};
export const getAllPages = async ({
token,
search = "",
}: {
token: string;
search?: string;
}) => {
if (!token) throw new Error("token is required");
const response = await notionFetch("https://api.notion.com/v1/search", {
method: "POST",
token: `Bearer ${token}`,
body: {
query: search,
filter: {
property: "object",
value: "page",
},
},
});
const pages: Page[] = (response?.results ?? []).map((result: any) => ({
id: result?.id,
parent: result.parent as Parent,
title: result?.properties?.title?.title?.[0]?.plain_text,
}));
return pages;
};
the Parent Object Document: https://developers.notion.com/reference/parent-object
Tips: the workspace
is the top level scope of Notion.
Result Structure#
[
{
"id": "107a42ed-c8f7-80d3-bbd1-deb70d057400",
"parent": {
"type": "workspace",
"workspace": true
},
"title": "Job Application Tracker"
},
{
"id": "107a42ed-c8f7-80e0-9f0f-ff3ddf9cb92d",
"parent": {
"type": "workspace",
"workspace": true
},
"title": "Parent Page"
},
{
"id": "0e68c737-dabc-474f-add3-de1a473e7eb3",
"parent": {
"type": "page_id",
"page_id": "107a42ed-c8f7-80e0-9f0f-ff3ddf9cb92d"
},
"title": "Child Page"
}
]
- Create New Page#
POST 'https://api.notion.com/v1/pages'
body.parent.page_id
orbody.parent.database_id
is required
- Create New Database#
POST 'https://api.notion.com/v1/databases'
body.parent.page_id
is required
Situations should be handled#
1. For Creating new Page or Database, both require the parent.page_id
, so we have to ask user to provide at least one page for our Integration.#
- Append Content to Page#
POST 'https://api.notion.com/v1/blocks/${pageId}/children'
body:
{
"children": [
// ...
]
}
Cases#
1. The redirect page - Error display#
display the error in redirect page after the notion install Integration page
2. The redirect page - Success and Waiting display#
time for us to using the url parameter code
exchange the notion user token.
And we should store the token into user DB
3. Require available page - There is no available page as the Root Page#
Since there could be no pages we can access, or user select some pages which NOT follow our rule(like page title should be xxxx
).
And Notion requires the parent.page_id
when create new pages. So we have to ask user to provide at least one available page for our Integration.
4. The SaveNotion Component at right_top of message box.#
4.1 Check the Notion is ready for user to save#
- No token -> jump to Notion Integration install page
- No available page -> jump to
3. Require available page
4.2 Save Notion#
How to store the content? things are better organized in Notion#
- Append the content into the Root Page directly
- Create Page (per conversation) under the Root Page. (Like case1 in demo video)
- 1.Create a Database under the Root Page 2. Cerate new page under the Database (Like case2 in demo video)
How to optimize the user experience?#
- remember the last save rule
- there will be
${input}(new page)
at the top of the list when there is no exact same title page. user click it we will create the new page and store the content in it.